其他
Bleeding Tooth:Linux蓝牙驱动远程代码执行分析与利用
本文为看雪论坛精华文章
一 • 原理
在上一篇分析中简要介绍了蓝牙协议栈结构,这两个漏洞涉及到蓝牙异步通信中的L2CAP协议、A2MP协议和和信道控制协议。
信道控制协议用于通信的双方协商L2CAP参数。
二 • CVE-2020-12352栈变量泄漏
//代码2-1 linux-5.4\net\bluetooth\a2mp.c
static int a2mp_getinfo_req(struct amp_mgr *mgr, struct sk_buff *skb, struct a2mp_cmd *hdr)
{
struct a2mp_info_req *req = (void *) skb->data;
struct hci_dev *hdev;
hdev = hci_dev_get(req->id);
if (!hdev || hdev->dev_type != HCI_AMP) {
struct a2mp_info_rsp rsp; //栈变量未初始化
rsp.id = req->id;
rsp.status = A2MP_STATUS_INVALID_CTRL_ID;
a2mp_send(mgr, A2MP_GETINFO_RSP, hdr->ident, sizeof(rsp),
&rsp);
}
三 • CVE-2020-12351从越界读到程序流劫持
//代码3-1 linux-5.4\net\bluetooth\l2cap_core.c
static void l2cap_data_channel(struct l2cap_conn *conn, u16 cid, struct sk_buff *skb)
{
struct l2cap_chan *chan;
chan = l2cap_get_chan_by_scid(conn, cid); //寻找对应的进程
if (!chan) {
if (cid == L2CAP_CID_A2MP) {
chan = a2mp_channel_create(conn, skb);
……
}
}
……
switch (chan->mode) {
……
case L2CAP_MODE_ERTM:
case L2CAP_MODE_STREAMING:
l2cap_data_rcv(chan, skb);
goto done;
}
//代码3-2 linux-5.4\net\bluetooth\l2cap_core.c
static int l2cap_data_rcv(struct l2cap_chan *chan, struct sk_buff *skb)
{
if ((chan->mode == L2CAP_MODE_ERTM || chan->mode == L2CAP_MODE_STREAMING)
&& sk_filter(chan->data, skb))
//代码3-3 linux-5.4\include\linux\filter.h
static inline int sk_filter(struct sock *sk, struct sk_buff *skb)
{
return sk_filter_trim_cap(sk, skb, 1);
}
//代码3-4 linux-5.4\net\bluetooth\a2mp.c
struct l2cap_chan *a2mp_channel_create(struct l2cap_conn *conn, struct sk_buff *skb)
{
struct amp_mgr *mgr;
……
mgr = amp_mgr_create(conn, false);
……
return mgr->a2mp_chan;
}
//代码3-5 linux-5.4\net\bluetooth\a2mp.c
static struct amp_mgr *amp_mgr_create(struct l2cap_conn *conn, bool locked)
{
struct amp_mgr *mgr;
struct l2cap_chan *chan;
mgr = kzalloc(sizeof(*mgr), GFP_KERNEL);
……
chan = a2mp_chan_open(conn, locked);
mgr->a2mp_chan = chan;
chan->data = mgr; //struct amp_mgr *
……
return mgr;
}
//代码3-6 linux-5.4\net\core\filter.c
int sk_filter_trim_cap(struct sock *sk, struct sk_buff *skb, unsigned int cap)
{
struct sk_filter *filter;
rcu_read_lock();
filter = rcu_dereference(sk->sk_filter);
if (filter) {
……
pkt_len = bpf_prog_run_save_cb(filter->prog, skb);
……
}
//代码3-7 linux-5.4\include\linux\filter.h
static inline u32 bpf_prog_run_save_cb(const struct bpf_prog *prog, struct sk_buff *skb)
{
u32 res;
preempt_disable();
res = __bpf_prog_run_save_cb(prog, skb);
preempt_enable();
return res;
}
//代码3-8 linux-5.4\include\linux\filter.h
static inline u32 __bpf_prog_run_save_cb(const struct bpf_prog *prog, struct sk_buff *skb)
{
……
res = BPF_PROG_RUN(prog, skb);
……
return res;
}
//代码3-9 linux-5.4\include\linux\filter.h
#define BPF_PROG_RUN(prog, ctx) ({ \
u32 ret; \
cant_sleep(); \
if (static_branch_unlikely(&bpf_stats_enabled_key)) { \
…… \
ret = (*(prog)->bpf_func)(ctx, (prog)->insnsi); \
…… \
} else { \
ret = (*(prog)->bpf_func)(ctx, (prog)->insnsi); \
} \
ret; })
向sk_filter传入指向struct amp_mgr的指针,通过堆喷射在后续堆块中sk->sk_filter的位置写入指向fake sk_filter的指针,prog指向fake bpf_prog,bpf_func指向rop gadget用于将内核栈劫持到rop chains的位置,如此便可以触发rop链劫持程序流。
四 • 构造目标和数据流
//代码4-1 linux-5.4\net\bluetooth\a2mp.c
static struct l2cap_chan *a2mp_chan_open(struct l2cap_conn *conn, bool locked)
{
chan = l2cap_chan_create();
……
chan->ops = &a2mp_chan_ops; //进程拥有的操作方法
……
chan->mode = L2CAP_MODE_ERTM; //chan->mode初始化为ERTM模式
return chan;
}
五 • 泄露内核代码段地址
六 • 泄露内核堆地址
这里将目标瞄准l2cap_chan结构。当cid=3会创建a2mp channel,
//代码6-1 linux-5.4\net\bluetooth\l2cap_core.c
static void l2cap_data_channel(struct l2cap_conn *conn, u16 cid, struct sk_buff *skb)
{
struct l2cap_chan *chan;
chan = l2cap_get_chan_by_scid(conn, cid); //寻找对应的进程
if (!chan) {
if (cid == L2CAP_CID_A2MP) {
chan = a2mp_channel_create(conn, skb);
……
}
}
//代码6-2 linux-5.4\net\bluetooth\l2cap_core.c
struct l2cap_chan *l2cap_chan_create(void)
{
struct l2cap_chan *chan;
chan = kzalloc(sizeof(*chan), GFP_ATOMIC);
if (!chan)
return NULL;
……
七 • 释放后重引用
//代码7-1 linux-5.4\net\bluetooth\l2cap_core.c
static void l2cap_chan_destroy(struct kref *kref)
{
struct l2cap_chan *chan = container_of(kref, struct l2cap_chan, kref);
write_lock(&chan_list_lock);
list_del(&chan->global_l);
write_unlock(&chan_list_lock);
kfree(chan);
}
//代码7-2 linux-5.4\net\bluetooth\a2mp.c
static int a2mp_getampassoc_rsp(struct amp_mgr *mgr, struct sk_buff *skb, struct a2mp_cmd *hdr)
{
struct a2mp_amp_assoc_rsp *rsp = (void *) skb->data;
u16 len = le16_to_cpu(hdr->len); //rsp+assoc_data
struct amp_ctrl *ctrl;
size_t assoc_len;
assoc_len = len - sizeof(*rsp); //assoc_data长度
if (rsp->status) //rsp->status字段保持为0
return -EINVAL;
ctrl = amp_ctrl_lookup(mgr, rsp->id);
if (ctrl) {
u8 *assoc;
assoc = kmemdup(rsp->amp_assoc, assoc_len, GFP_KERNEL); //分配堆块
if (!assoc) {
amp_ctrl_put(ctrl);
return -ENOMEM;
}
ctrl->assoc = assoc;
ctrl->assoc_len = assoc_len;
ctrl->assoc_rem_len = assoc_len;
ctrl->assoc_len_so_far = 0;
amp_ctrl_put(ctrl);
}
//代码7-3 linux-5.4\net\bluetooth\a2mp.c
static int a2mp_getinfo_rsp(struct amp_mgr *mgr, struct sk_buff *skb, struct a2mp_cmd *hdr)
{
struct a2mp_info_rsp *rsp = (struct a2mp_info_rsp *) skb->data;
struct a2mp_amp_assoc_req req;
struct amp_ctrl *ctrl;
if (rsp->status)
return -EINVAL;
ctrl = amp_ctrl_add(mgr, rsp->id);
1、构造堆喷射
要注意的是ACL数据的最大传输单元为1021字节,也就是每次传输的L2CAP数据分组不超过1021字节,对于要喷射0x400(1024字节)大小的载荷需要分组传输。
同时要注意a2mp channel缓冲区默认大小为L2CAP_DEFAULT_MIN_MTU(670字节),也就是l2cap携带的amp载荷不超过670字节。
//代码7-4 linux-5.4\net\bluetooth\a2mp.c
static struct l2cap_chan *a2mp_chan_open(struct l2cap_conn *conn, bool locked)
{
struct l2cap_chan *chan;
chan = l2cap_chan_create();
……
chan->imtu = L2CAP_A2MP_DEFAULT_MTU;
……
//代码7-5 linux-5.4\net\bluetooth\l2cap_core.c
static void l2cap_data_channel(struct l2cap_conn *conn, u16 cid, struct sk_buff *skb)
{
switch (chan->mode) {
……
case L2CAP_MODE_BASIC:
if (chan->imtu < skb->len) { //检查amp包长度是否大于mtu
goto drop;
}
if (!chan->ops->recv(chan, skb)) //调用a2mp_chan_recv_cb
goto done;
2、第一次堆喷射
由于旧的a2mp channel极有可能被再次占用,所以选择偏移0x400的位置也就是紧邻的下一个堆块保存fake bpf_prog和ROP链。
3、构造fake sk_filter、 bpf_prog和ROP链
fake sk_filter位于堆块偏移0x300处,sk_filter->prog指向0xffff_8882_090d_ef20即为:
0xffffffff8155528d push rsi
add byte ptr [rbx + 0x41], bl
pop rsp
pop rbp
ret
八 • 第二次堆喷射
amp_mgr结构大小为0x70,slab实际会分配0x80字节的堆块存放amp_mgr。
九 • 总结
官方Exploit需要三次堆喷射,经我调试改进之后只需2次堆喷即可。
十 • 参考资料
[1] BleedingTooth: Linux Bluetooth Zero-Click Remote Code Execution
[2]《深入Linux内核架构》Wolfgang Mauerer著
[3]《BLUETOOTH SPECIFICATION Version 5.0 》
[4] Exploit:https://pan.baidu.com/s/1b9Zez-kRy8opubcLKxNRtw 密码: 0610
看雪ID:极目楚天舒
https://bbs.pediy.com/user-home-741085.htm
*本文由看雪论坛 极目楚天舒 原创,转载请注明来自看雪社区。
# 往期推荐
球分享
球点赞
球在看
点击“阅读原文”,了解更多!